/*
 * tcp_helper.c
 *
 *  Created on: May 9, 2016
 *      Author: Alan Wang
 */

#include "tcp_helper.h"
#include <sys/socket.h>
#include <arpa/inet.h>

#include "../../util/log.h"
#include "../../util/str_util.h"

#define BUFFER_SIZE 1024

int PACK_HEADER_LENTH = 16;
char PACK_HEADER_HEAD[2] = {0x06, 0xA1};//protocol header
char PACK_HEADER_REMAIN[10] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};//10 byte reamian data
char PACK_HEADER_DATA_LEN[4] = {0x00, 0x00, 0x00, 0x00};//4 byte json data lenth
char recv_buf[BUFFER_SIZE];
char cache_buf[BUFFER_SIZE];
int cache_len = 0;
char tmp_buf[BUFFER_SIZE];


int get_content_length(char* pack_header, int len);
void get_pack_header(char** pack_header, int json_len);
void package_json_data(char** pack_data, char* json_str);
int get_pack_head_position(char* header, int len);

/**
 * init server ntify socket
 */
int init_tcp_socket(int socket_port) {
//	socket_port = 15346;
	Log("init_tcp_socket()-->%d\n", socket_port);
	int tcp_socket_fd;
	struct sockaddr_in tcp_socket_addr;
	//creat server socket
	tcp_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (tcp_socket_fd < 0) {
	    LogStr("open socket failed !");
	    return -1;
	}
	LogStr("initialize socket structure\n");
	// initialize socket structure
	bzero((char *) &tcp_socket_addr, sizeof(tcp_socket_addr));
	tcp_socket_addr.sin_family = AF_INET;
	tcp_socket_addr.sin_port = htons(socket_port);
	tcp_socket_addr.sin_addr.s_addr = htonl(INADDR_ANY);//get host ip as the addr

	LogStr("bind addr\n");
	//bind addr
    if(bind(tcp_socket_fd, (struct sockaddr *)&tcp_socket_addr, sizeof(tcp_socket_addr)) == -1) {
    	LogStr("bind socket failed !\n");
        return -1;
    }

    Log("listen socket client--->>>tcp_socket_fd = %d\n", tcp_socket_fd);
    //listen socket client
    if(listen(tcp_socket_fd, 20) == -1) {
    	LogStr("listen socket failed !\n");
        return -1;
    }
    return tcp_socket_fd;
}

/**
 *receive data from client socket
 */
int recv_data_from_client(char** json_content_str, int client_socket_fd){
	int recv_len = 0;
	int total = 0;
	int header_len = 0;
	int header_offset = 0;
	int content_len = -1;
	int content_offset = 0;
	int tmp_len = 0;
	char recv_head_arr[PACK_HEADER_LENTH];

	//deal cache buf data
	if(cache_len > 0){//if have cache data, find the valide data by PACK_HEADER_HEAD
		Log("cache_len = %d\n", cache_len);
		int i, pos;
		pos = get_pack_head_position(cache_buf, cache_len);
		if(pos >= 0){//if have valide data, copy to recv buf,
			recv_len = content_len -pos;
			pstr_copy(recv_buf, 0, cache_buf, pos, recv_len);
		}
		memset(cache_buf, 0, BUFFER_SIZE);//clear the cache buffer and cache_len when have dealed the cache data
		cache_len = 0;
		prt_pstr_in_hex(recv_buf, recv_len);
	}

	do{
		if(content_len < 0 && header_len <= 0 && recv_len > 0){//find pack header
			int head_pos = get_pack_head_position(recv_buf, recv_len);
			Log("do()---->>>head_pos = %d\n", head_pos);
			if(head_pos > 0){
				recv_len = recv_len - head_pos;
				pstr_copy(tmp_buf, 0, recv_buf, head_pos, recv_len);
				pstr_copy(recv_buf, 0, tmp_buf, 0, recv_len);
			}else if(head_pos < 0){
				recv_len = 0;
			}
		}

		total+=recv_len;
		tmp_len = 0;
		header_offset = 0;
		if(header_len < PACK_HEADER_LENTH){//add pack header
			if(header_len + recv_len > PACK_HEADER_LENTH){
				tmp_len = PACK_HEADER_LENTH - header_len;
			}else{
				tmp_len = recv_len;
			}
			pstr_copy(recv_head_arr, header_len, recv_buf, 0, tmp_len);

			header_len += tmp_len;
			header_offset += tmp_len;
		}
		if(content_len < 0 && header_len >= PACK_HEADER_LENTH){//have read package header and have not gotten the content length
			//prt_pstr_in_hex(recv_head_arr, PACK_HEADER_LENTH);

			content_len = get_content_length(recv_head_arr, PACK_HEADER_LENTH);
			//Log("content_len = %d\n", content_len);
			*json_content_str = (char*)malloc(content_len + 1);//malloc to save json data
		}
		recv_len -= header_offset;
		if(content_len > 0 && recv_len > 0){
			if(content_offset + recv_len > content_len){
				tmp_len = content_len - content_offset;
				Log("recv_len = %d\n", recv_len);
				Log("content_offset = %d\n", content_offset);
				Log("content_len = %d\n", content_len);

				//save remain data
				cache_len = content_offset + recv_len - content_len;
				Log("--->>>>>cache_len = %d\n", cache_len);
				pstr_copy(cache_buf, 0, recv_buf, header_offset + tmp_len, cache_len);
			}else{
				tmp_len = recv_len;
			}
			pstr_copy(*json_content_str, content_offset, recv_buf, header_offset, tmp_len);
			content_offset += tmp_len;
		}
		if(total >= content_len + PACK_HEADER_LENTH){
			break;
		}

		recv_len = recv(client_socket_fd, recv_buf, sizeof(recv_buf), 0);
		Log("recv_len = %d\n", recv_len);
		//prt_pstr_in_hex(recv_buf, recv_len);
		if(recv_len <= 0){
			break;
		}

	}while(1);
	if(content_len > 0){
		*(*json_content_str + content_len) = '\0';
	}
	Log("recv_data_from_client()---->>>content_len = %d\n", content_len);
	return content_len;
}

/**
 * send data to client socket
 */
int send_data_to_client(int client_socket_fd, char* json_str){
	Log("send json_str--->>>%s\n", json_str);
	int ret = -1;
	if(client_socket_fd > 0){
		char* send_data;
		package_json_data(&send_data, json_str);
		int send_data_len = PACK_HEADER_LENTH + strlen(json_str);
		ret = send(client_socket_fd, send_data, send_data_len, 0);
		if(ret < 0){
			LogStr("send json data failed\n");
			free(send_data);
			return ret;
		}
		Log("successfully send %d byte data!\n", ret);
		free(send_data);
	}
	return ret;
}


/**
 * get the json content length from 16 byte header
 */
int get_content_length(char* pack_header, int len){
	if(len < PACK_HEADER_LENTH){
		return 0;
	}
	char content_len_arr[4] = {0};
	pstr_copy(content_len_arr, 0, pack_header, 12, 4);//copy the 4 byte conten length data to content_head
	int content_len = big_endian_to_int(content_len_arr, 4);
	return content_len;
}

/**
 * get 16 byte package heaer with json_len
 */
void get_pack_header(char** pack_header, int json_len){
	*pack_header = (char*)malloc(PACK_HEADER_LENTH);
	int_to_big_endian(PACK_HEADER_DATA_LEN, json_len);
	pstr_copy(*pack_header, 0, PACK_HEADER_HEAD, 0, 2);
	pstr_copy(*pack_header, 2, PACK_HEADER_REMAIN, 0, 10);
	pstr_copy(*pack_header, 12, PACK_HEADER_DATA_LEN, 0, 4);
}

/**
 * package json data with package header and json str
 */
void package_json_data(char** pack_data, char* json_str){
	int json_len = strlen(json_str);
	int total_len = PACK_HEADER_LENTH + json_len;
	Log("json_len = %d\n",json_len);
	*pack_data = (char*)malloc(total_len + 1);
	char* pack_head;
	get_pack_header(&pack_head, json_len);
	pstr_copy(*pack_data, 0, pack_head, 0, PACK_HEADER_LENTH);//copy package header
	pstr_copy(*pack_data, PACK_HEADER_LENTH, json_str, 0, strlen(json_str));//copy json data
	//prt_pstr_in_hex(*pack_data, total_len);
	free(pack_head);
}

/**
 * get the head position of the packet
 */
int get_pack_head_position(char* header, int len){
	int pos = -1;
	int i;
	for(i=0; i<len-1; i++){
		if(*(header+i) == PACK_HEADER_HEAD[0] && *(header+i+1) == PACK_HEADER_HEAD[1]){
			pos = i;
			break;
		}
	}
	return pos;
}
